#include "mainform.h"
#include "ui_mainform.h"

MainForm::MainForm(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::MainForm)
{
    ui->setupUi(this);

    this->connect_slots();

    // init spinbox
    this->ui->iou_spinbox->setRange(0, 1);
    this->ui->iou_spinbox->setSingleStep(0.01);
    this->ui->iou_slider->setTracking(false);
    this->ui->conf_spinbox->setRange(0, 1);
    this->ui->conf_slider->setTracking(false);
    this->ui->conf_spinbox->setSingleStep(0.01);

    // init slider
    this->ui->iou_slider->setRange(0, 100);
    this->ui->iou_slider->setSingleStep(1);
    this->ui->conf_slider->setRange(0, 100);
    this->ui->conf_slider->setSingleStep(1);

    // 固定窗口大小
    this->setWindowTitle("Yolo v8 检测");
    // this->setWindowIcon(QIcon(":/icon/HPU.png"));
    this->setFixedSize(this->width(), this->height());

    // 设置默认IOU和CONF
    this->reset_iou_slot();
    this->reset_conf_slot();
    timer= new QTimer(this);
    timer->start(500);
    connect(timer, SIGNAL(timeout), this, SLOT(onTimeout));
    // matQueue=new ThreadSafeQueue();
    // 要在主程序初始化该对象
    if (inference == nullptr)
    {
        Inference* infrens = new Inference();

        inference.reset(infrens);
    }
    // 另起线程加载模型
    this->refresh_model_list();   // 刷新下拉列表
    // 如果模型文件 combox有列表项
    // 选中第一个 触发选中事件
    emit this->ui->model_select->activated(0);  // 加载第一个模型

}

MainForm::~MainForm()
{
    delete ui;
}

void MainForm::connect_slots()
{
    QObject::connect(ui->cammera_button, SIGNAL(clicked()), this, SLOT(cammera_slot()));
    QObject::connect(ui->image_button, SIGNAL(clicked()), this, SLOT(image_slot()));
    QObject::connect(ui->video_button, SIGNAL(clicked()), this, SLOT(video_slot()));

    QObject::connect(ui->iou_spinbox, SIGNAL(valueChanged(double)), this, SLOT(iou_spinbox_slot(double)));
    QObject::connect(ui->conf_spinbox, SIGNAL(valueChanged(double)), this, SLOT(conf_spinbox_slot(double)));

    QObject::connect(ui->iou_slider, SIGNAL(valueChanged(int)), this, SLOT(iou_slider_slot(int)));
    QObject::connect(ui->conf_slider, SIGNAL(valueChanged(int)), this, SLOT(conf_slider_slot(int)));

    QObject::connect(ui->reset_iou_button, SIGNAL(clicked()), this, SLOT(reset_iou_slot()));
    QObject::connect(ui->reset_conf_button, SIGNAL(clicked()), this, SLOT(reset_conf_slot()));
    QObject::connect(ui->model_select, SIGNAL(activated(int)), this, SLOT(model_select_slot(int)));
    QObject::connect(ui->import_model_button, SIGNAL(clicked()), this, SLOT(import_model_button_slot()));
    QObject::connect(ui->import_model_label_button, SIGNAL(clicked()), this, SLOT(import_model_label_button_slot()));

    // QObject::connect(this, SIGNAL(start_loading_movie_signal()), this, SLOT(start_loading_movie_slot()));
    // QObject::connect(this, SIGNAL(stop_loading_movie_signal()), this, SLOT(stop_loading_movie_slot()));
}

void MainForm::onTimeout()
{
    // this->ui->show_detect->animate();
}

void MainForm::onmodelMessage(QString err_msg)
{
    this->append_log(err_msg);
}

void MainForm::onPixmapChanged(QPixmap &mp)
{

}
// slots
void MainForm::cammera_slot()
{
    releaseRunning();
    if (!this->check_model()) {  // 模型检测未通过
        return;
    }

    bool oslabe = this->ui->islabel->isChecked();;
     videoCaptureDetectThread  =new VideoCaptureDetectThread(this);
    videoCaptureDetectThread->init(this->inference,run_with_cuda,oslabe,this->matQueue);
    // videoDetectThread = videoCaptureDetectThread;
    connect(videoCaptureDetectThread,&VideoCaptureDetectThread::imageReady,[this]( QPixmap &pixmap){
        this->show_img(pixmap);
    });
    connect(videoCaptureDetectThread, SIGNAL(onMessage(QString )), this, SLOT(onmodelMessage(QString )));
    videoCaptureDetectThread->start();
    //QThreadPool::globalInstance()->start(videoCaptureDetectThread);
    //videoCaptureDetectThread.wait();
     // cvmatDetectThread = new CvmatDetectThread(this);
    // videoFrmReadThread=cvmatDetectThread;
    // 设置 接收图片事件
    // connect(cvmatDetectThread,&CvmatDetectThread::imageReady,[this]( QPixmap &pixmap){
    //     this->show_img(pixmap);
    // });
    // cvmatDetectThread->init(this->inference, run_with_cuda,oslabe,this->matQueue);
    // cvmatDetectThread->start();
    //QThreadPool::globalInstance()->start(cvmatDetectThread);
    //cvmatDetectThread.wait();
}

void MainForm::image_slot()
{
    releaseRunning();
    if (!this->check_model()) {  // 模型检测未通过
        return;
    }
    // 选择图片文件
    QString img_file = QFileDialog::getOpenFileName(this, "Open File", ".", "Images(*.png *.jpg)");
    if (img_file == "") {
        return;
    }
    // 检测
    // 是否标出
    auto radio_button = this->ui->islabel;
    if (this->inference == nullptr) {
        QMessageBox::warning(this, "错误", "未检测到模型");
        return;
    }

    // 开始
    start_loading();
    cv::Mat frame = cv::imread(img_file.toStdString());
    this->inference->doPredict(frame, radio_button->isChecked());
    QPixmap pixmap = commons::cvMatToQPixmap(frame);
    // 停止
    stop_loading(1,img_file,"image");
    show_img(pixmap);

}

void MainForm::video_slot()
{
    releaseRunning();
    if (!this->check_model()) {  // 模型检测未通过
        return;
    }
    QString videofile = QFileDialog::getOpenFileName(this, "Open File", ".", "Videos(*.mp4)");
    if (videofile == "") {
        return;
    }

    bool oslabe = this->ui->islabel->isChecked();
    videoFileDetectThread   =new VideoFileDetectThread(this);
    videoFileDetectThread->init(this->inference,videofile,run_with_cuda,oslabe);

    connect(videoFileDetectThread,&VideoFileDetectThread::imageReady,[this]( QPixmap &pixmap){
        this->show_img(pixmap);
    });
    connect(videoFileDetectThread, SIGNAL(onMessage(QString)), this, SLOT(onmodelMessage(QString)));
    videoFileDetectThread->start();
}

void MainForm::iou_spinbox_slot(double value)
{
    this->ui->iou_slider->blockSignals(true);
    this->ui->iou_slider->setValue(int(value*100));
    this->ui->iou_slider->blockSignals(false);
}

void MainForm::conf_spinbox_slot(double value)
{
    this->ui->conf_slider->blockSignals(true);
    this->ui->conf_slider->setValue(int(value * 100));
    this->ui->conf_slider->blockSignals(false);

}

void MainForm::iou_slider_slot(int value) {
    this->ui->iou_spinbox->blockSignals(true);
    this->ui->iou_spinbox->setValue(value * 1.0 / 100);
    this->ui->iou_spinbox->blockSignals(false);
}

void MainForm::conf_slider_slot(int value) {
    this->ui->conf_spinbox->blockSignals(true);
    this->ui->conf_spinbox->setValue(value * 1.0 / 100);
    this->ui->conf_spinbox->blockSignals(false);
}

void MainForm::reset_iou_slot()
{
    this->iou_slider_slot(50);
    this->iou_spinbox_slot(0.5);
}

void MainForm::reset_conf_slot()
{
    this->conf_slider_slot(45);
    this->conf_spinbox_slot(0.45);
}

void MainForm::model_select_slot(int index)
{
    if (index == this->current_weight_index or this->ui->model_select->count() == 0) {
        return;
    }
    this->current_weight_index = index;
    QString filepath = this->ui->model_select->itemText(index);

    float iou = this->ui->iou_spinbox->value();
    float conf = this->ui->conf_spinbox->value();
    cv::Size input_shape = cv::Size(640, 640);
    bool run_with_cuda=false;
    // 加载模型
    ModelLoader *worker = new ModelLoader(this);

    worker->init(this->inference,run_with_cuda,"weights/" + filepath,iou,conf,input_shape);
    connect(worker, SIGNAL(workstarted()), this,
            SLOT(start_loading()));
    connect(worker, SIGNAL(workfinished(int ,QString ,QString )), this,
            SLOT(stop_loading(int ,QString ,QString )));

    //QThreadPool::globalInstance()->start(worker);
    worker->start();
    // // 等待线程执行完毕
    worker->wait();
    // worker->deleteLater();
}

void MainForm::import_model_button_slot()
{
    QString file = QFileDialog::getOpenFileName(this, "Open File", ".", "Models(*.onnx)");
    if (file == "") {
        return;
    }
    bool copy_success = commons::copy_weights(file, "weights");
    if (copy_success) {
        this->refresh_model_list();
        //this->append_log("导入成功");
        // 如果是 onnx 文件, 选中导入的模型

        if (file.endsWith(".onnx")) {
            std::string file_name = commons::get_file_name(file.toStdString());;
            int index = this->ui->model_select->findText(QString::fromStdString(file_name));
            if (index != -1) {
                this->current_weight_index = -1; // 重新加载模型
                emit this->ui->model_select->activated(index);
            }
        }
    }
}
void MainForm::import_model_label_button_slot()
{
    QString file = QFileDialog::getOpenFileName(this, "Open File", ".", "Models(*.txt)");
    if (file == "") {
        return;
    }
    bool copy_success = commons::copy_weights(file, "weights");
    if (copy_success) {
        //this->refresh_model_list();
        //this->append_log("导入成功");
        // 如果是 onnx 文件, 选中导入的模型

        if (file.endsWith(".onnx")) {
            std::string file_name = commons::get_file_name(file.toStdString());;
            int index = this->ui->model_select->findText(QString::fromStdString(file_name));
            // if (index != -1) {
            //     this->current_weight_index = -1; // 重新加载模型
            //     emit this->ui->model_select->activated(index);
            // }
        }
    }
}

void MainForm::start_loading()
{
    // 显示个遮罩 ??
    this->disable_all_button();
}

void MainForm::stop_loading (int staus,QString filepath,QString msg)
{

    this->enable_all_button();
    QString msgg="load ";
    msgg.append(filepath);
    ui->output->append(msgg);
    qDebug()<<"stop loading";
}

void MainForm::islabel_slot(bool value)
{
    this->ui->islabel->setChecked(value);
}



void MainForm::refresh_model_list()
{
    QDir dir("weights");
    QStringList filters;
    filters << "*.onnx";
    QStringList files = dir.entryList(filters);
    ui->model_select->clear();
    ui->model_select->addItems(files);
}

void MainForm::disable_all_button()
{
    this->ui->cammera_button->setDisabled(true);
    this->ui->image_button->setDisabled(true);
    this->ui->video_button->setDisabled(true);
    this->ui->model_select->setDisabled(true);
    this->ui->import_model_button->setDisabled(true);

}

void MainForm::enable_all_button()
{
    this->ui->cammera_button->setDisabled(false);
    this->ui->image_button->setDisabled(false);
    this->ui->video_button->setDisabled(false);
    this->ui->model_select->setDisabled(false);
    this->ui->import_model_button->setDisabled(false);
}

bool MainForm::check_model()
{
    // 模型不存在
    if (this->inference == nullptr and this->ui->model_select->count() == 0) {
        QMessageBox::warning(this, "Error", QString("未检测到模型"));
        return false;
    }


    // 模型存在,检测txt文件
    std::string base_name = commons::get_base_name(this->ui->model_select->currentText().toStdString());
    std::string txt_name = base_name + ".txt";
    std::string txt_path = "weights/" + txt_name;
    if (!QFile::exists(QString::fromStdString(txt_path))) {
        QString msg;
        msg = msg + "未检测到" + txt_name.c_str() + ", 请导入...";
        ui->output->append(msg);
        QMessageBox::warning(this, "Error", QString::fromStdString(txt_name) + "不存在");
        return false;
    }
    if (this->inference != nullptr) {
        this->inference->loadClassesFromFile();
    }
    return true;
}

void MainForm::load_model_from_model_list()
{
    int index = this->ui->model_select->currentIndex();
    this->model_select_slot(index);
}

void MainForm::show_img(QPixmap &pixmap)
{
    if (pixmap.isNull()) {
        QMessageBox::warning(this, "Error", "Failed to load image");
        return;
    }
    int width = ui->show_detect->width();
    int height = ui->show_detect->height();
    float rate = 0.8f;
    ui->show_detect->setAlignment(Qt::AlignCenter);
    ui->show_detect->setPixmap(pixmap.scaled(int(width * rate), int(height * rate), Qt::KeepAspectRatio, Qt::FastTransformation));
}

void MainForm::append_log(QString &text)
{
    ui->output->append(text);
}

void MainForm::closeEvent(QCloseEvent* event)
{
    qDebug()<<"form close.";

    timer->stop();
    releaseRunning();
    //this->matQueue->clear();

    qDebug()<<"form close 2.";
    //QThreadPool::globalInstance()->waitForDone();


    // QDeadlineTimer timer;
    // timer.setDeadline(1000);
    // QThreadPool::globalInstance()->waitForDone(timer);
}

void MainForm::releaseRunning()
{
    if(videoCaptureDetectThread!=nullptr){
        videoCaptureDetectThread->stop();
        videoCaptureDetectThread->release();
        videoCaptureDetectThread->terminate();
        cv::waitKey(100);
    }
    if(videoFileDetectThread!=nullptr){
        videoFileDetectThread->stop();
        videoFileDetectThread->release();
        videoFileDetectThread->terminate();
        cv::waitKey(100);
    }
    if(videoCaptureDetectThread!=nullptr){
        delete videoCaptureDetectThread;
        videoCaptureDetectThread=nullptr;
    }
    if(videoFileDetectThread!=nullptr){
        delete videoFileDetectThread;
        videoFileDetectThread=nullptr;
    }
}
